package NajaTaskClient.Conexao;

import com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author NidorX
 */
public class DbTabela implements Comparable<DbTabela> {

    String nome;
    Set<DbTabelaColuna> Colunas;

    /**
     * Instancia um objeto tabela
     * @param nome
     */
    public DbTabela(String nome) {
        /*
         *  CREATE TABLE IF NOT EXISTS fornecedores (
        Codigo int(11) NOT NULL,
        Nome text NOT NULL,
        Codigo_departamento int(11) NOT NULL,
        PRIMARY KEY  (Codigo)       
        )  ";
         */
        this.nome = nome;
        this.Colunas = new TreeSet<DbTabelaColuna>();
    }

    /**
     *
     * @return
     */
    public Set<DbTabelaColuna> obterColunas() {
        return this.Colunas;
    }

    /**
     *
     * @param o
     * @return
     */
    public int compareTo(DbTabela o) {
        final int EQUAL = 0;
        final int AFTER = 1;
        if (this == o) {
            return EQUAL;
        }
        if (this.nome.equals(o.nome)) {
            // System.out.println("ERRO: Não foi possivel inserir a coluna \"" + o.nome + "\"");
            //System.out.println("\t Motivo: Ja existe uma coluna com esse nome e com a seguinte configuração");
            //System.out.print("\t\t");
            //this.exibeDados();
            return EQUAL;
        }
        return AFTER;
    }

    /**
     * Obtem o nome da tabela atual
     * @return String
     */
    public String obterNome() {
        return this.nome;
    }

    /**
     *
     * @param coluna
     * @return
     */
    public DbTabelaColuna coluna(String coluna) {
          System.out.println("teste dentro de dbTbCol");
        DbTabelaColuna col = null;
        for (DbTabelaColuna e : this.Colunas) {
            if (e.obterNome().equals(coluna)) {
                col = e;
                break;
            }
        }
        return col;

    }

    /**
     * retorna os valores contidos na tabela atual
     * @param argumento

     * @return
     */
    public ResultSet selectAll() {
        return Db.get().dbTabelaSelectAll(this.obterNome());
        //return "";
    }

    /**
     * retorna os valores contidos na tabela atual
     * @param argumento

     * @return
     */
    public ResultSet selectAllClausulaWhere(String clausula) {
         
        return Db.get().dbTabelaSelectAllWhere(this.obterNome(), clausula);
    }

    /**
     * Insere no banco de dados , na tabela atual ( se ela existir no banco de
     * dados ) os valores definidos. Ela testa antes de adicionar , se dentro
     * dessa tabela existem as colunas informadas , se nao existir , ela ignora ,
     * e se o tipo de dado for diferente , criará uma exessao
     * exemplo de uso :
     *  tabelaX.insert("nome='alex',texto='José's d',valor=25");
     * A funçao trata essa string e a transforma em algo como:
     *  INSERT INTO nome_tabela (nome,texto,valor) VALUES('alex','José\'s d',25);
     * @param valores
     * @return true ou false
     * @throws SQLException
     * @throws ExcecaoGenericaMensagem
     */
    public boolean insert(String valores) throws SQLException, ExcecaoGenericaMensagem {
        /*
         * lógica:
         * verificar se o tipo da coluna é NOT NULL , se for , verificar se existe
         * argumento para ele . Se nao existir , emitir erro
         * 1º transformar a string em um array , separado por virgula
         * 2º em cada elemento do array separar o conteudo do nome da coluna
         * 3º para cada nome de coluna , verificar se realmente existe e se o tipo
         * de dado informado é compativel
         * 5º tratar todo o texto de conteudo para inserir no banco de dados
        
         * 6º criar a requisição sql
         * 7º executar o comando sql 
         */
        Vector<String> aRemover = new Vector<String>();
        Map<String, String> chaveValor = new LinkedHashMap<String, String>();

        //explodindo a string
        String[] valoresArr = valores.split("\\',");
        // removendo o "'" du último array
        valoresArr[valoresArr.length - 1] = valoresArr[valoresArr.length - 1].substring(0, valoresArr[valoresArr.length - 1].length() - 1);
        //para cada expressao , separar nome da coluna do valor passado
        for (int i = 0; i < valoresArr.length; i++) {
            // quebrando as string dos arrays anteriores em outro array menor
            // já com chave=valor
            String[] tmp = valoresArr[i].split("\\='");
            // testar se o tamanho de tmp = 2 , se for maior nao faz nada
            //pois existe erro
            if (tmp.length == 2) {
                chaveValor.put(tmp[0], tmp[1]);
            }
            //System.out.println(tmp[0]+tmp[1]);
        }

        //para cada elemento chaveValor , verificar se existe uma coluna para ela
        Set<Map.Entry<String, String>> set = chaveValor.entrySet();
        Iterator<Map.Entry<String, String>> it = set.iterator();
        // verificando todo o map chaveValor para saber se existe nas colunas
        // desta tabela , se nao houver , adciona aos que serão removidos
        while (it.hasNext()) {
            Map.Entry<String, String> me = it.next();
            boolean existe = false;
            for (DbTabelaColuna e : this.Colunas) {
                if (me.getKey().equals(e.obterNome())) {
                    existe = true;
                }
            }
            // se nao existir , remover o elemento do set chaveValor
            if (!existe) {
                aRemover.add(me.getKey());
            }
        }

        //verificando todas as colunas dessa tabela se existem elementos NOT NULL
        // se houver , verificar se foi passado argumentos para ela , senao , emitir erro
        for (DbTabelaColuna col : this.Colunas) {
            // not null e não auto increment
            if (!col.obterNulo() && !col.obterAutoIncrementa()) {
                //verifica todos os parametros para saber se existe um valor para a coluna atual
                it = set.iterator();
                boolean existe = false;
                while (it.hasNext()) {
                    Map.Entry<String, String> me = it.next();
                    if (me.getKey().equals(col.obterNome())) {
                        existe = true;
                        break;
                    }
                }
                if (!existe) {
                    throw new ExcecaoGenericaMensagem("A coluna \"" + col.obterNome() + "\" da tabela \"" + this.obterNome() + "\"necessita de algum argumento pois ela é NOT NULL");
                }
            }
        }
        // remover do map todos os elementos que nao existem nessa tabela
        ListIterator<String> iter = aRemover.listIterator();
        while (iter.hasNext()) {
            chaveValor.remove(iter.next());
            // System.out.println(iter.next());
        }
        try {
            Db.get().dbTabelaInsert(this.obterNome(), chaveValor);
        } catch (MySQLIntegrityConstraintViolationException e) {
            e.printStackTrace();
        }
        // if(Db.get().dbTabelaInsert(this.obterNome(), valores, valores))
        //imprimindo os elementos
        it = set.iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> me = it.next();
            System.out.println(me.getKey() + " : " + me.getValue());
            //aRemover.add(me.getKey());
        }


        return false;
    }

    /**
     * Remove do banco de dados a linha em que a clausula satisfaça
     * @param clausula exemplo nome='alex' ou ano>2005
     * @return
     */
    public boolean removeLinhaClausualWhere(String clausula) {
        if (Db.get().dbTabelaColunaRemoveLinhaWhereClausula(this.obterNome(), clausula)) {
            return true;
        }
        return false;
    }

    public boolean atualizarLinhaClausulaWhere(String valores, String clausula) {
        if (Db.get().dbTabelaColunaAtualizaLinhaClausulaWhere(this.obterNome(), valores, clausula)) {
            return true;
        }
        return false;
    }

    /**
     * Adiciona uma nova coluna a tabela atual
     * @param campo
     * @return true ou false
     * @throws ExcecaoGenericaMensagem
     */
    public boolean campoAdicionar(DbTabelaColuna campo) throws ExcecaoGenericaMensagem {
        if (campo.obterChavePrimaria()) {
            //testar se esse é definido como chave primaria , se for , veriricar
            // se ja foi definido outro como chave primaria
            boolean existe = false;
            String colNome = null;
            for (DbTabelaColuna e : this.Colunas) {
                if (e.obterChavePrimaria()) {
                    existe = true;
                    colNome = e.obterNome();
                    break;
                }
            }
            if (existe) {
               // throw new ExcecaoGenericaMensagem("Já foi definida antes a coluna \"" + colNome + "\" como Chave Primária");
            }

        }
        if (!this.Colunas.add(campo)) {
            throw new ExcecaoGenericaMensagem("Não foi possivel inserir a coluna \"" + campo.obterNome() + "\""
                    + " na tabela \"" + this.obterNome() + "\""
                    + "\nMotivo: Já existe uma coluna com esse nome");
        }
        campo.setarTabela(this);
        return true;
    }

    /**
     *
     * @param args
     */
    public static void main(String[] args) {
        DbTabela teste = new DbTabela("tabela02");
        try {
            teste.campoAdicionar(new DbTabelaColuna("codigo", "INT", 10, false, true, true));
            teste.campoAdicionar(new DbTabelaColuna("nome", "VARCHAR", 11, false, false, false));
            teste.campoAdicionar(new DbTabelaColuna("quantidade", "INT", 11, false, false, false));
        } catch (ExcecaoGenericaMensagem ex) {
            teste.imprimeColunas();
            Logger.getLogger(DbTabela.class.getName()).log(Level.SEVERE, null, ex);
        }

        //
    }

    /**
     * imprime todos os dados da tabela atual . Apenas para testes
     */
    public void exibetDados() {
        System.out.println("Colunas existentes na tabela \"" + this.nome + "\"");
        this.imprimeColunas();
    }

    /**
     * Imprime o conjunto de colunas da tabela atual . Apenas para testes
     */
    public void imprimeColunas() {
        for (DbTabelaColuna e : this.Colunas) {
            e.exibeDados();
        }
    }
}
